package com.service.gmall.Item.service.impl;

import com.service.gmall.feign.product.SkuDetailFeignClient;
import com.service.starter.cache.annotation.GmallCache;
import com.service.starter.cache.service.CacheOpsService;
import com.service.gmall.Item.service.SkuDetailsService;
import com.service.gmall.common.constant.SysRedisConst;
import com.service.gmall.common.result.Result;
import com.service.gmall.common.util.Jsons;
import com.service.gmall.model.product.SkuImage;
import com.service.gmall.model.product.SkuInfo;
import com.service.gmall.model.product.SpuSaleAttr;
import com.service.gmall.model.to.CategoryViewTo;
import com.service.gmall.model.to.SkuDetailsTo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author alpha
 * @className: SkuDetailsServiceImpl
 * @date 2022/8/26 15:01
 * @Description
 */
@Slf4j
@Service
public class SkuDetailsServiceImpl implements SkuDetailsService {
    @Autowired
    SkuDetailFeignClient skuDetailFeignClient;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    CacheOpsService cacheOpsService;

    ReentrantLock lock = new ReentrantLock();
    @Autowired
    ThreadPoolExecutor executor;
    private Map<Long, SkuDetailsTo> skuCache = new ConcurrentHashMap<>();

    public SkuDetailsTo getSkuDetailsFromRpc(Long skuId) {
        SkuDetailsTo skuDetailsTo = new SkuDetailsTo();
        //远程调用商品服务查询

        //1.分步查询，分解单一接口压力
        //1.查询基本信息
        CompletableFuture<SkuInfo> skuInfoFuture = CompletableFuture.supplyAsync(() -> {

            Result<SkuInfo> skuInfoResult = skuDetailFeignClient.getSkuInfo(skuId);
            SkuInfo skuInfo = skuInfoResult.getData();
            skuDetailsTo.setSkuInfo(skuInfo);
            return skuInfo;
        }, executor);
        //2.查询商品图片信息
        CompletableFuture<Void> imagesFuture = skuInfoFuture.thenAcceptAsync((skuInfo) -> {
            if (skuInfo != null) {

                Result<List<SkuImage>> skuImages = skuDetailFeignClient.getSkuImages(skuId);
                skuInfo.setSkuImageList(skuImages.getData());
            }
        }, executor);

        //3.查询商品价格
        CompletableFuture<Void> priceFuture = CompletableFuture.runAsync(() -> {
            Result<BigDecimal> skuRealTimePrice = skuDetailFeignClient.getSkuRealTimePrice(skuId);
            skuDetailsTo.setPrice(skuRealTimePrice.getData());
        }, executor);

        //4.查询销售属名和值
        CompletableFuture<Void> spuSaleAttrFuture = skuInfoFuture.thenAcceptAsync((skuInfo) -> {
            if (skuInfo != null) {

                Result<List<SpuSaleAttr>> spuSaleAttrList = skuDetailFeignClient.getSpuSaleAttrList(skuInfo.getSpuId(), skuId);
                skuDetailsTo.setSpuSaleAttrList(spuSaleAttrList.getData());
            }
        }, executor);
        //5.查询sku组合
        CompletableFuture<Void> valueJsonFuture = skuInfoFuture.thenAcceptAsync((skuInfo) -> {
            if (skuInfo != null) {

                Result<String> valuesSkuJson = skuDetailFeignClient.getValuesSkuJson(skuInfo.getSpuId());
                skuDetailsTo.setValuesSkuJson(valuesSkuJson.getData());
            }
        }, executor);
        //6.查询分类
        CompletableFuture<Void> categoryFuture = skuInfoFuture.thenAcceptAsync((skuInfo) -> {
            if (skuInfo != null) {

                Result<CategoryViewTo> categoryView = skuDetailFeignClient.getCategoryView(skuInfo.getCategory3Id());
                skuDetailsTo.setCategoryView(categoryView.getData());
            }
        }, executor);

        //同时完成
        CompletableFuture
                .allOf(imagesFuture, priceFuture, spuSaleAttrFuture, valueJsonFuture, categoryFuture)
                .join();
        return skuDetailsTo;
    }

//    @Transactional

    /**
     * params表示方法的所有参数列表
     *
     * @param skuId
     * @return
     */
    @GmallCache(cacheKey = SysRedisConst.SKU_INFO_PREFIX + "#{#params[0]}",
            bloomName = SysRedisConst.BLOOM_SKUID,
            bloomValue = "#{#params[0]}",
            lockName = SysRedisConst.LOCK_SKU_DETAIL + "#{#params[0]}",
            ttl = 60*60*24*7)
    @Override
    public SkuDetailsTo getDetails(Long skuId) {
        SkuDetailsTo fromRpc = getSkuDetailsFromRpc(skuId);
        return fromRpc;
    }

    public SkuDetailsTo getDetailsWithCache(Long skuId) {
        String cacheKey = SysRedisConst.SKU_INFO_PREFIX + skuId;
        //1.先查缓存
        SkuDetailsTo cacheData = cacheOpsService.getCacheData(cacheKey, SkuDetailsTo.class);
        if (cacheData == null) {
            //2.缓存不命中
            //3.查询布隆
            boolean exist = cacheOpsService.bloomContains(skuId);
            if (!exist) {
                //4.布隆中不存在，直接返回nulll
                log.info("[{}]商品 - 布隆判定没有，检测到隐藏的攻击风险....", skuId);
                return null;
            }
            //5.布隆中有，准备回源
            boolean lock = cacheOpsService.tryLock(skuId);
            if (lock) {
                //6.获取锁成功，回源
                log.info("[{}]商品 缓存未命中，布隆说有，准备回源.....", skuId);
                SkuDetailsTo fromRpc = getSkuDetailsFromRpc(skuId);
                //7.写回缓存
                cacheOpsService.saveData(cacheKey, fromRpc);
                //8.解锁
                cacheOpsService.unLock(skuId);
                //查到返回
                return fromRpc;
            }
            //9.没获取到锁
            try {
                Thread.sleep(1000);
                return cacheOpsService.getCacheData(cacheKey, SkuDetailsTo.class);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        //缓存命中，直接返回
        return cacheData;
    }

    public SkuDetailsTo getxxDetails(Long skuId) {
        //1.查缓存
        String jsonStr = stringRedisTemplate.opsForValue().get("sku:info:" + skuId);
        if ("x".equals(jsonStr)) {//x表示缓存占位符
            return null;
        }
        //2.缓存没命中
        if (StringUtils.isEmpty(jsonStr)) {
            //2.1回源
            SkuDetailsTo fromRpc = null;
            //加锁
            boolean b = this.lock.tryLock();
            if (b) {
                //抢到锁
                fromRpc = getSkuDetailsFromRpc(skuId);
            } else {
                //没抢到锁
//                Thread.sleep(10000L);
                jsonStr = stringRedisTemplate.opsForValue().get("sku:info:" + skuId);
            }
            //2.2同步缓存
            stringRedisTemplate.opsForValue().set("sku:info:" + skuId, Jsons.toStr(fromRpc));
        }
        //3.缓存命中
        SkuDetailsTo skuDetailsTo = Jsons.toObj(jsonStr, SkuDetailsTo.class);


        return skuDetailsTo;
    }

    public SkuDetailsTo getxDetails(Long skuId) {
        //1.查缓存
        String jsonStr = stringRedisTemplate.opsForValue().get("sku:info:" + skuId);
        //2.缓存没命中
        if (StringUtils.isEmpty(jsonStr)) {
            //2.1回源
            SkuDetailsTo fromRpc = getSkuDetailsFromRpc(skuId);
            //2.2同步缓存
            stringRedisTemplate.opsForValue().set("sku:info:" + skuId, Jsons.toStr(fromRpc));
        }
        //3.缓存命中
        SkuDetailsTo skuDetailsTo = Jsons.toObj(jsonStr, SkuDetailsTo.class);


        return skuDetailsTo;
    }


//    /**
//     * 使用本地缓存
//     * @param skuId
//     * @return
//     */
//    @Override
//    public SkuDetailsTo getDetails(Long skuId) {
//        //先查缓存数据
//        SkuDetailsTo cacheData = skuCache.get(skuId);
//        //缓存没命中
//        if (cacheData == null){
//            //缓存没有，回源
//            SkuDetailsTo fromRpc = getSkuDetailsFromRpc(skuId);
//            skuCache.put(skuId,fromRpc);
//        }
//        //缓存命中，直接返回
//        return cacheData;
//    }

//    @Override
//    public SkuDetailsTo getDetails(Long skuId) {
////        SkuDetailsTo skuDetailsTo = new SkuDetailsTo();
//        //远程调用商品服务查询
//        Result<SkuDetailsTo> skuDetails = skuDetailFeignClient.getSkuDetails(skuId);
//        return skuDetails.getData();
//    }
}
